{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 在 iJavaScript 内核的 Jupyter Notebook 中使用 D3.js\n",
    "\n",
    "因为 iJavascript 是基于 Node.js 技术的，因此在 iJavaScript 内核的 Jupyter Notebook 中使用 D3.js，就必然与直接在 HTML 中使用 D3.js 存在区别，本文的目的在于讨论如何以最简洁的方式实现 D3.js 库在 Jupyter Notebook 中的应用。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## iJavaScript 中输出并渲染 HTML\n",
    "\n",
    "\n",
    "iJavaScript的文档(<http://n-riesco.github.io/ijavascript/doc/custom.ipynb.html>)中，介绍了 iJavaScript 中生成 HTML 内容的方式：`$$.html(htmlString)`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id='demo' style='background-color:#ccc;width:50px;height:50px'></div>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "$$.html(\"<div id='demo' style='background-color:#ccc;width:50px;height:50px'></div>\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，让我们按照在浏览器中正常开发的方式，加载d3.js库，并试图输出一个svg图片。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<script src=\"https://cdn.bootcss.com/d3/5.15.1/d3.min.js\"></script>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "$$.html('<script src=\"https://cdn.bootcss.com/d3/5.15.1/d3.min.js\"></script>')\n",
    "var w = 500;\n",
    "var h = 50;\n",
    "var svg = d3.select(\"body\")\n",
    "    .append(\"svg\")\n",
    "    .attr(\"width\", w)\n",
    "    .attr(\"height\", h);\n",
    "console.log(document)\n",
    "\n",
    "$$.html(document.body.innerhtml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过上面的案例，我们发现直接引入脚本并运行的方式，并不能达到效果，因为在nodejs中，并没有像浏览器一样存在`window.document`这样的全局变量。故使用d3js虽能生成选集，但是由于没有document对象，故而无法在nodejs中输出网页内容。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在nodejs中使用DOM\n",
    "\n",
    "为了能在 Jupyter Notebook 中使用JavaScript中的DOM，我们需要引入[jsdom](https://github.com/jsdom/jsdom)这样的库。jsdom 是一个可在 node.js 中模拟浏览器，以便对真实 web 应用程序进行测试和筛选的 JavaScript 工具。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "按照 jsdom 文档的说明，我们引入 jsdom 后，声明一个在 nodejs 中的全局变量 document，然后再通过 nodejs，引入 d3。\n",
    "\n",
    "```\n",
    "npm install d3 jsdom\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Document { location: [Getter/Setter] }"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "var jsdom = require('jsdom');\n",
    "const { JSDOM } = jsdom;\n",
    "const { document } = (new JSDOM('')).window;\n",
    "global.document = document;\n",
    "var d3 = require(\"d3\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面的运行结果可以看出，已经在 nodejs 中生成了一个标准的 document 对象。接下来就可以用正常的方式，进行 d3 的操作了。比如创建一个 svg 元素，并在里面画一个圆。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"25\" fill=\"#ccc\"></circle></svg>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "var svg = d3.select(document.body).append('svg')\n",
    "    .attr('xmlns', 'http://www.w3.org/2000/svg')\n",
    "    .attr('width', 100)\n",
    "    .attr('height', 100);\n",
    "\n",
    "svg.append(\"circle\")\n",
    "    .attr(\"cx\", 50)\n",
    "    .attr(\"cy\", 50)\n",
    "    .attr(\"r\", 25)\n",
    "    .attr(\"fill\", \"#ccc\");\n",
    "\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考资料\n",
    "\n",
    "1.  <http://n-riesco.github.io/ijavascript/doc/custom.ipynb.html>\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Javascript (Node.js)",
   "language": "javascript",
   "name": "javascript"
  },
  "language_info": {
   "file_extension": ".js",
   "mimetype": "application/javascript",
   "name": "javascript",
   "version": "13.13.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
